//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <xllp_lcd.h>
#include <bulverde.h>
#include <drvpub.h>
#include "display_def.h"

#define CLEAR_LCD_INTR(reg, intr) do {  \
    reg = (intr);           \
} while(0)

#define WAIT_FOR_LCD_INTR(reg,intr,timeout) ({  \
    int __done =0;              \
    int __t = timeout;          \
    while (__t) {               \
        __done = (reg) & (intr);    \
        if (__done) break;      \
        DzDelay(10 * 1000);         \
        __t--;              \
    }                   \
    if (!__t) DBGOUT(DBG_LOG, ("wait " #intr " time out\n"));\
    __done;                 \
})

#if 0
void DumpLcdControllerRegs()
{
    cprintf("dump lcd:\n");
    cprintf("LCCR0=%08x\n", LCCR0);
    cprintf("LCCR1=%08x\n", LCCR1);
    cprintf("LCCR2=%08x\n", LCCR2);
    cprintf("LCCR3=%08x\n", LCCR3);
    cprintf("LCCR4=%08x\n", LCCR4);
    cprintf("LCCR5=%08x\n", LCCR5);

    cprintf("OVL1C1=%08x\n", OVL1C1);
    cprintf("OVL1C2=%08x\n", OVL1C2);
    cprintf("OVL2C1=%08x\n", OVL2C1);
    cprintf("OVL2C2=%08x\n", OVL2C2);
}
#endif

static void LCDClearStatusReg()
{
    // Clear the status registers by writing 1's to each bit.
    LCSR0 = ( LCD_LDD | LCD_SOF0| LCD_BER | LCD_ABC | LCD_IU0   |
                          LCD_IU1 | LCD_OU  | LCD_QD  | LCD_EOF0| LCD_BS0   |
                          LCD_SINT| LCD_RD_ST | LCD_CMD_INTR );

    LCSR1 = ( LCD_SOF1| LCD_SOF2| LCD_SOF3| LCD_SOF4| LCD_SOF5  | LCD_SOF6  |
                          LCD_EOF1| LCD_EOF2| LCD_EOF3| LCD_EOF4| LCD_EOF5  |
                          LCD_EOF6  |
                          LCD_BS1 | LCD_BS2 | LCD_BS3 | LCD_BS4 | LCD_BS5   |
                          LCD_BS6   |
                          LCD_IU2 | LCD_IU3 | LCD_IU4 | LCD_IU5   | LCD_IU6 );
}

// When enable/disable overlay, judge whether to reset the controller here.
void LcdCtrl_OverlayResetController(
                    DisplayContext *pCxt, OverlayFormat *pOverlay)
{
    Boolean bReinit = FALSE; // Judge whether reinit LCD controller
    UInt32 valPDFOR = (LCCR3 >> 30);

    if (pOverlay) {
        if (pCxt->overlayType == OVERLAYTYPE_UNDERLAY) {
            if (LCCR0 & LCD_OUC) bReinit = TRUE;
        }
        else {
            if ((LCCR0 & LCD_OUC) == 0) bReinit = TRUE;
        }
    }

    if (pCxt->basePlaneFormat == COLOR_RGB565) {
        if (valPDFOR != PDFOR_00) {
            bReinit = TRUE;
        }
        DBGOUT(DBG_OVERLAY, ("Baseplane: RGB565 "));
    }
    else {
        if (valPDFOR != PDFOR_11) {
            bReinit = TRUE;
        }
         DBGOUT(DBG_OVERLAY, ("Baseplane: RGB555T "));
    }

    if (bReinit) {
        DBGOUT(DBG_OVERLAY, ("And reinit LCD controller."));
        LcdCtrl_Suspend(pCxt, 0);
        LcdCtrl_Resume(pCxt);
    }
    DBGOUT(DBG_OVERLAY, ("\n"));
}

void LcdCtrl_Init(DisplayContext *pCxt)
{
    int PCD = 0;
    unsigned int val_LCCR0;
    int val_BPP;
    int PixelDataFormat;

    if (pCxt->basePlaneFormat == COLOR_RGB565) {
        PixelDataFormat = PDFOR_00;
    }
    else {
        PixelDataFormat = PDFOR_11; // RGBT555
    }

    LCCR0 = 0;
    LCCR1 = 0;
    LCCR2 = 0;
    LCCR3 = 0;
    LCCR4 = 0;
    LCCR5 = (LCD_SOFM1|LCD_SOFM2|LCD_SOFM3|LCD_SOFM4|LCD_SOFM5|LCD_SOFM6|
                        LCD_EOFM1|LCD_EOFM2|LCD_EOFM3|LCD_EOFM4|LCD_EOFM5|
                        LCD_EOFM6|LCD_BSM1 |LCD_BSM2 |LCD_BSM3 |LCD_BSM4 |
                        LCD_BSM5 |LCD_BSM6 |LCD_IUM1 |LCD_IUM2 |LCD_IUM3 |
                        LCD_IUM4 |LCD_IUM5 |LCD_IUM6 );

    // Determine the frame buffer size for the DMA transfer length.
    // Scale the size based on the bpp of the frame buffer to determine
    // an actual size in bytes
    int FrameBufferSize = LCD_WIDTH * LCD_HEIGHT;
    FrameBufferSize <<= 1; // 16BPP

    // Enable the LCD
    CKEN = (CKEN & XLLP_CLKEN_MASK) | CLK_LCD ;

    LCD_FRAME_DESCRIPTOR *frameDescriptorCh0fd1 =
            (LCD_FRAME_DESCRIPTOR *)pCxt->vaChan0Desc;

    // Configure the general purpose frame descriptors
    // Set the physical address of the frame descriptor
    frameDescriptorCh0fd1->FDADR = LCD_FDADR(pCxt->paChan0Desc);

    // Set the physical address of the frame buffer
    frameDescriptorCh0fd1->FSADR = LCD_FSADR(pCxt->paBasePlane);

    // Clear the frame ID
    frameDescriptorCh0fd1->FIDR  = LCD_FIDR(0);

    // Set the DMA transfer length to the size of the frame buffer
    frameDescriptorCh0fd1->LDCMD = LCD_Len(FrameBufferSize);

    // FBR0 is cleared and is not used.
    FBR0 = 0;

    // Load the contents of FDADR0 with the physical address of this frame descriptor
    FDADR0 = LCD_FDADR(frameDescriptorCh0fd1->FDADR);


#if 0
    unsigned int val_CCCR_L = 0;
    int LCLK = 0;
    // Determine the LCLK frequency programmed into the CCCR.
    // This value will be used to calculate a Pixel Clock Divisor (PCD)
    // for a given display type.
    val_CCCR_L = (CCCR & 0x0000001F);

    if (val_CCCR_L < 8) // L = [2 - 7]
        LCLK = (13 * val_CCCR_L) * 100;
    else if (val_CCCR_L < 17) // L = [8 - 16]
        LCLK = ((13 * val_CCCR_L) * 100) >> 1; // THIS
    else if (val_CCCR_L < 32) // L = [17 - 31]
        LCLK = ((13 * val_CCCR_L) * 100) >> 2;
#endif

    // Convert the bpp setting into a value that the LCD controller understands.
    val_BPP = O_BPP_16;
    PCD = 8;

    // Configure the LCD Controller Control Registers
    val_LCCR0 = (LCD_LDM | LCD_SFM | LCD_IUM | LCD_EFM |
            LCD_PAS | LCD_QDM | LCD_BM  | LCD_OUM |
            LCD_RDSTM | LCD_CMDIM | LCD_LDDALT);

    if (pCxt->overlayType == OVERLAYTYPE_OVERLAY) {
        val_LCCR0 |= LCD_OUC;
    }

    LCCR0 = val_LCCR0;

#if defined(_NEW_LCD_DRIVER)
    // s6d0139

    PCD = 8;

    LCCR1 = (LCD_PPL(0xEF) | LCD_HSW(0x5) |
            LCD_ELW(0x1)  | LCD_BLW(0x1) );

    LCCR2 = (LCD_LPP(0x13f) | LCD_VSW(0x5) |
            LCD_EFW(0x1)  | LCD_BFW(0x1) );
    LCCR3 = (LCD_PCD(PCD)  | LCD_BPP(val_BPP) | LCD_PCP | LCD_VSP | LCD_HSP |
            LCD_PDFOR(PixelDataFormat));
//    LCCR3 = 0x04800008;
#elif defined(_OLD_LCD_DRIVER)
    // AA022QD02
    LCCR1 = (LCD_PPL(0xEF) | LCD_HSW(0x2) |
            LCD_ELW(0x2)  | LCD_BLW(0x4) );

    LCCR2 = (LCD_LPP(0x13f) | LCD_VSW(0x1) |
            LCD_EFW(0x1)  | LCD_BFW(0x1) );

    LCCR3 = (LCD_PCD(PCD)  | LCD_BPP(val_BPP) | LCD_PCP |LCD_VSP | LCD_HSP |
            LCD_PDFOR(PixelDataFormat));
//    LCCR3 = 0x04700008;
#else
    // S1D19120
    LCCR1 = (LCD_PPL(0xEF) | LCD_HSW(0x1) |
            LCD_ELW(0x1)  | LCD_BLW(0x1) );

    LCCR2 = (LCD_LPP(0x13f) | LCD_VSW(0x2) |
            LCD_EFW(0x1)  | LCD_BFW(0x1) );
    LCCR3 = (LCD_PCD(PCD)  | LCD_BPP(val_BPP) | LCD_OEP |
            LCD_PDFOR(PixelDataFormat));
//    LCCR3 = 0x04800008;
#endif

    LCCR4 |= LCD_K1(1) | LCD_K2(1) | LCD_K3(1);

    // Clear LCD Controller status register
    LCDClearStatusReg();
}

void LcdCtrl_Enable(DisplayContext *pCxt)
{
    LCCR0 |= LCD_ENB;
}

void LcdCtrl_Disable(DisplayContext *pCxt)
{
    LCCR0 &= ~LCD_ENB;
}

static ECode LcdCtrl_SetOverlay1(DisplayContext *pCxt, OverlayFormat *pOverlay)
{
    volatile LCD_FRAME_DESCRIPTOR *pChan1Desc;
    UInt32 _Bpp;

    assert(pOverlay->Color == COLOR_RGBT666 || pOverlay->Color == COLOR_RGB565);

    pChan1Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan1Desc;

    switch (pOverlay->Color) {
        case COLOR_RGBT666:
            _Bpp = 0x7;
            break;

        case COLOR_RGB565:
        default:
            _Bpp = 0x4;
            break;
    }

    // Configure the general purpose frame descriptors
    // Set the physical address of the frame descriptor
    pChan1Desc->FDADR = LCD_FDADR(pCxt->paChan1Desc);

    // Set the physical address of the frame buffer
    pChan1Desc->FSADR = LCD_FSADR(pCxt->paOverlay1);

    // Clear the frame ID
    pChan1Desc->FIDR  = LCD_FIDR(0);

    // Set the DMA transfer length to the size of the frame buffer
    pChan1Desc->LDCMD = LCD_Len(pCxt->uChan1Size);

    // disable branch/start/end of frame interrupt
    LCCR5 |= (LCD_IUM1 | LCD_BSM1 | LCD_EOFM1 | LCD_SOFM1);

    OVL1C2 = LCD_O1YPOS(pOverlay->PosY) | LCD_O1XPOS(pOverlay->PosX);
    OVL1C1 = (LCD_O1EN | LCD_BPP1(_Bpp) |
        LCD_LPO1(pOverlay->Height-1) | LCD_PPL1(pOverlay->Width-1));

#if 0
    // Can't reach here now.
    // Reserved the code here for future use.
    if (pCxt->overlay[1].Enable ) {
        FBR1 = LCD_FDADR(pChan1Desc->FDADR) | LCD_BRA;
        DBGOUT(DBG_OVERLAY, ("LcdCtrl_SetOverlay1: reconfigure overlay1\n"));
    }
    else
#endif
    {
        FDADR1 = LCD_FDADR(pChan1Desc->FDADR);
        DBGOUT(DBG_OVERLAY, ("LcdCtrl_SetOverlay1: initialize overlay1\n"));

    }

#if 0
        if (pCxt->overlay[2].Enable) {
            // If overlay1 is set, must reinit DMA transfer for overlay2.
            volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;

            pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
            pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
            pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

            FDADR2 = LCD_FDADR(pChan2Desc->FDADR);
            FDADR3 = LCD_FDADR(pChan3Desc->FDADR);
            FDADR4 = LCD_FDADR(pChan4Desc->FDADR);
        }
#endif

    return NOERROR;
}

static ECode LcdCtrl_SetOverlay2(DisplayContext *pCxt, OverlayFormat *pOverlay)
{
    unsigned int Format;
    volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;

    assert(pOverlay->Color == COLOR_YCbCr422 ||
        pOverlay->Color == COLOR_YCbCr420);

    pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
    pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
    pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

    switch (pOverlay->Color) {
        case COLOR_YCbCr422:
            Format = FORMAT_PLANAR_422;
            break;

        case COLOR_YCbCr420:
        default:
            Format = FORMAT_PLANAR_420;
            break;
    }

    // Configure the general purpose frame descriptors
    // Set the physical address of the frame descriptor
    pChan2Desc->FDADR = LCD_FDADR(pCxt->paChan2Desc);
    pChan3Desc->FDADR = LCD_FDADR(pCxt->paChan3Desc);
    pChan4Desc->FDADR = LCD_FDADR(pCxt->paChan4Desc);

    // Set the physical address of the frame buffer
    pChan2Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Y);
    pChan3Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Cb);
    pChan4Desc->FSADR = LCD_FSADR(pCxt->paOverlay2Cr);

    // Clear the frame ID
    pChan2Desc->FIDR  = LCD_FIDR(0);
    pChan3Desc->FIDR  = LCD_FIDR(0);
    pChan4Desc->FIDR  = LCD_FIDR(0);

    // Set the DMA transfer length to the size of the frame buffer
    pChan2Desc->LDCMD = LCD_Len(pCxt->uChan2Size);
    pChan3Desc->LDCMD = LCD_Len(pCxt->uChan3Size);
    pChan4Desc->LDCMD = LCD_Len(pCxt->uChan4Size);

    // disable branch/start/end of frame interrupt
    LCCR5 |= (LCD_IUM4 | LCD_IUM3 | LCD_IUM2 |
          LCD_BSM4 | LCD_BSM3 | LCD_BSM2 |
          LCD_EOFM4 | LCD_EOFM3 | LCD_EOFM2 |
          LCD_SOFM4 | LCD_SOFM3 | LCD_SOFM2);

    OVL2C2 = (LCD_FOR(Format) | LCD_O2YPOS(pOverlay->PosY) |
        LCD_O2XPOS(pOverlay->PosX));
    OVL2C1 = (LCD_O2EN | LCD_BPP2(pOverlay->Bpp) |
        LCD_LPO2(pOverlay->Height - 1) | LCD_PPL2(pOverlay->Width - 1));

    // FIX PROBLEM FOR IU2 IN YCbCr420 mode
    LCSR1 = LCD_IU2;
    // WAIT_FOR_LCD_INTR(LCSR1, LCD_IU2, 100);

#ifdef OVERLAY2_CAN_REENABLE
    if (pCxt->overlay[2].Enable ) {
        // This code may lead to white screen
        FBR2 = LCD_FDADR(pChan2Desc->FDADR) | LCD_BRA;
        FBR3 = LCD_FDADR(pChan3Desc->FDADR) | LCD_BRA;
        FBR4 = LCD_FDADR(pChan4Desc->FDADR) | LCD_BRA;
        DBGOUT(DBG_OVERLAY, ("LcdCtrl_SetOverlay2: reconfigure overlay2\n"));
    }
    else
#endif
    {
        FDADR2 = LCD_FDADR(pChan2Desc->FDADR);
        FDADR3 = LCD_FDADR(pChan3Desc->FDADR);
        FDADR4 = LCD_FDADR(pChan4Desc->FDADR);
        DBGOUT(DBG_OVERLAY, ("LcdCtrl_SetOverlay2: initialize overlay2\n"));
    }

#if 0
    if (pCxt->overlay[1].Enable) {
        // If overlay2 is set, must reinit DMA transfer for overlay1.
        volatile LCD_FRAME_DESCRIPTOR *pChan1Desc;
        pChan1Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan1Desc;
        FDADR1 = LCD_FDADR(pChan1Desc->FDADR);
    }
#endif

    return NOERROR;
}

ECode LcdCtrl_SetOverlay(DisplayContext *pCxt, OverlayFormat *pOverlay)
{
    assert(pOverlay->Layer == 1 || pOverlay->Layer == 2);

    if (pOverlay->Layer == 1) {
        return LcdCtrl_SetOverlay1(pCxt, pOverlay);
    }
    else {
        return LcdCtrl_SetOverlay2(pCxt, pOverlay);
    }
}

static void LcdCtrl_DisableOverlay1(DisplayContext *pCxt)
{
    volatile LCD_FRAME_DESCRIPTOR *pChan1Desc;

    pChan1Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan1Desc;

    if (!(OVL1C1 & LCD_O1EN)) {
        // Not enable
        return;
    }

    DBGOUT(DBG_OVERLAY, ("LcdCtrl_DisableOverlay1\n"));

    OVL1C1 &= 0x00FFFFFF; // Clear the enable bit, and clear the reserved bits 30:24.

    CLEAR_LCD_INTR(LCSR1, LCD_BS1);
    FBR1 = 0x3;
    int done = WAIT_FOR_LCD_INTR(LCSR1, LCD_BS1, 100);

    if (!done) {
        DBGOUT(DBG_ERR, ("Timeout for disable overlay1, reenable LCD.\n"));
        LcdCtrl_Resume(pCxt);
    }

#if 0
    if (pCxt->overlay[2].Enable) {
        // If overlay1 is disabled, must reinit DMA transfer for overlay2.
        volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;

        pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
        pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
        pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

        FDADR2 = LCD_FDADR(pChan2Desc->FDADR);
        FDADR3 = LCD_FDADR(pChan3Desc->FDADR);
        FDADR4 = LCD_FDADR(pChan4Desc->FDADR);
    }
#endif

}

static void LcdCtrl_DisableOverlay2(DisplayContext *pCxt)
{
    volatile LCD_FRAME_DESCRIPTOR *pChan2Desc, *pChan3Desc, *pChan4Desc;
    OverlayFormat *pOverlay;

    pOverlay = &pCxt->overlay[2];

    pChan2Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan2Desc;
    pChan3Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan3Desc;
    pChan4Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan4Desc;

    if (!(OVL2C1 & LCD_O2EN)) {
        // Not enable
        return;
    }

    DBGOUT(DBG_OVERLAY, ("LcdCtrl_DisableOverlay2\n"));

    OVL2C1 &= 0x00FFFFFF; // Clear the enable bit, and clear the reserved bits 30:24.

    /* This sentence was lost in opt.patch.
     * That make overlay2 can't disable/enable
     * correctly sometimes.
     */
    CLEAR_LCD_INTR(LCSR1, LCD_BS2);

    FBR2 = 0x3;
    FBR3 = 0x3;
    FBR4 = 0x3;

    int done = WAIT_FOR_LCD_INTR(LCSR1, LCD_BS2, 100);

    if (!done) {
        DBGOUT(DBG_ERR, ("Timeout for disable overlay2, reenable LCD.\n"));
        LcdCtrl_Resume(pCxt);    
    }

#if 0
    if (pCxt->overlay[1].Enable) {
        // If overlay2 is disabled, must reinit DMA transfer for overlay1.
        volatile LCD_FRAME_DESCRIPTOR *pChan1Desc;
        pChan1Desc = (volatile LCD_FRAME_DESCRIPTOR *)pCxt->vaChan1Desc;
        FDADR1 = LCD_FDADR(pChan1Desc->FDADR);
    }
#endif

}

void LcdCtrl_DisableOverlay(DisplayContext *pCxt, int layer)
{
    assert(layer == 1 || layer == 2);

    if (layer == 1) {
        LcdCtrl_DisableOverlay1(pCxt);
    }
    else {
        LcdCtrl_DisableOverlay2(pCxt);
    }
}

void LcdCtrl_Suspend(DisplayContext *pCxt, int type)
{
    if (LCCR0 & LCD_ENB) {
        switch (type) {
            case 0:{// Gracefullly
                // Initiate power down sequence
                LCCR0 |= LCD_DIS;
                // Wait for LDD bit to get set once the last DMA transfer has completed
                DzDelay(20 * 1000);

                int counter = 0;
                while (!(LCSR0 & LCD_LDD)) {
                    DBGOUT(DBG_ERR, ("LcdCtrl_Suspend: not ready,"\
                        " FDADR0 0x%x FSADR0 0x%x FIDR0 0x%x LDCMD0 0x%x \n",
                        FDADR0, FSADR0, FIDR0, LDCMD0));
                    counter++;
                    if (counter > 20) {
                        LCCR0 &= ~LCD_ENB;
                        break;
                    }
                }

                // Clear the sticky LDD bit
                LCSR0 |= LCD_LDD;
                break;
            }

            case 1: // Immediately
                LCCR0 &= ~LCD_ENB;
                break;

            default :
                break;
        }
    }
}

void LcdCtrl_Resume(DisplayContext *pCxt)
{
    LcdCtrl_Init(pCxt);
    LcdCtrl_Enable(pCxt);
}

void LcdCtrl_SetTransLevel(DisplayContext *pCxt, Byte level)
{
    level = 7 - level / 32; // [0, 255] -> [7, 0]

    // Clear K1, K2, K3 bits
    LCCR4 &= ~(LCD_K1(7) | LCD_K2(7) | LCD_K3(7));

    LCCR4 |= LCD_K1(level) | LCD_K2(level) | LCD_K3(level);
}
